package ignoreurl

import (
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"unsafe"

	"gitee.com/tomatomeatman/golang-repository/bricks2/function/fileutil"
	"gitee.com/tomatomeatman/golang-repository/bricks2/model/msgentity"
	"gitee.com/tomatomeatman/golang-repository/bricks2/utils/app"
	"gitee.com/tomatomeatman/golang-repository/bricks2/utils/ginutil"
	"gitee.com/tomatomeatman/golang-repository/bricks2/utils/gorm"
	Log "github.com/cihub/seelog"
)

/**
 * 拦截器忽略路径IgnoreUrl表基本业务操作结构体
 */
type IgnoreUrlService struct {
	app.ServiceBaseFunc
}

/**
 * 简版'忽略路径'结构体
 */
type SimpleIgonoreURL struct {
	IgnoreUrl    string `json:"ignoreUrl"`
	MustLogin    int64  `json:"mustLogin"`
	OnlyUserType string `json:"onlyUserType"`
}

var (
	cacheSetNotLogin    map[string]string //路径缓存(全部)免登录
	cacheSubSetNotLogin map[string]string //路径缓存(仅包含/*的路径)免登录
	cacheSetLogin       map[string]string //路径缓存(全部)必须登录
	cacheSubSetLogin    map[string]string //路径缓存(仅包含/*的路径)必须登录
	checkWrite          sync.Mutex        //保存锁
)

// 初始化
func init() {
	go IgnoreUrlService{}.initCacheMap() //初始化缓存
}

/**
 * 验证指定url是否在可忽略的访问路径中(给内部拦截器用,直接返回Boolean)
 * @param url 待检验的url
 * @param mustLogin 是否必须登录
 * @param userType 待检验的用户类型
 * @return
 */
func (iu IgnoreUrlService) CheckIgnoreUrl(url string, mustLogin bool, userType string) *msgentity.MsgEntity {
	if strings.TrimSpace(url) == "" {
		return msgentity.Err(8001, "请求检验的Url参数为空")
	}

	iu.initCacheMap() //初始化缓存

	if userType != "" {
		userType = ";" + userType + ";"
	} else {
		userType = ""
	}

	//mustLogin = (null == mustLogin || mustLogin.intValue() != 1) ? 2 : 1;//1为必须登录

	//--在免登录的明确路径中找--//
	if _, ok := cacheSetNotLogin[url]; ok {
		userTypeList := cacheSetNotLogin[url] //权限限定的用户类型集合
		if userTypeList == "" {
			return msgentity.Success(true, "在免登录的免拦截中发现,并且不限定用户")
		}

		if strings.Contains(userTypeList, userType) {
			return msgentity.Success(true, "在免登录的免拦截中发现,但是限定了用户") //在免登录的免拦截中发现,但是限定了用户
		}
	}

	//--在免登录的明确路径中中没找到,则可能属于子路径请求--//
	for key, userTypeList := range cacheSubSetNotLogin {
		if !strings.Contains(url, key) {
			continue
		}

		if userTypeList == "" {
			return msgentity.Success(true, "在免登录的免拦截中发现,并且不限定用户")
		}

		if strings.Contains(userTypeList, userType) {
			return msgentity.Success(true, "在免登录的免拦截中发现,但是限定了用户") //在免登录的免拦截中发现,但是限定了用户
		}
	}

	if !mustLogin { //未指定要"必须登录"
		return msgentity.Success(false, "在指定项目中都没有找到可以免拦截的约定") // MsgEntity.err(8003, "在指定项目中都没有找到可以免拦截的约定")//不符合
	}

	//--指定要"必须登录"--//

	for key, userTypeList := range cacheSetLogin {
		if url != key {
			continue
		}

		if userTypeList == "" {
			return msgentity.Success(true, "在必须登录的免拦截(明确路径)中发现,并且不限定用户")
		}

		if !strings.Contains(userTypeList, userType) {
			continue //拦截路径的使用限定了用户类型
		}

		return msgentity.Success(true, "在必须登录的免拦截(明确路径)中发现,并且限定用户")
	}

	for key, userTypeList := range cacheSubSetLogin {
		if !strings.Contains(url, key) {
			continue
		}

		if userTypeList == "" {
			return msgentity.Success(true, "在必须登录的免拦截(子路径)中发现,并且不限定用户")
		}

		if !strings.Contains(userTypeList, userType) {
			continue
		}

		return msgentity.Success(true, "在必须登录的免拦截(子路径)中发现,并且限定用户")
	}

	return msgentity.Success(false, "在指定项目中都没有找到可以免拦截的约定") //不符合
}

/**
 * 初始化缓存
 * @return
 */
func (iu IgnoreUrlService) initCacheMap() {
	if len(cacheSetNotLogin) > 0 {
		return
	}

	checkWrite.Lock()         //加锁
	defer checkWrite.Unlock() //解锁

	if len(cacheSetNotLogin) > 0 {
		return
	}

	filePath := "./temp/cache/IgnoreUrl/IgnoreUrl.json"
	if fileutil.IsExist(filePath) {
		bl, _, da := fileutil.ReadFromFile(filePath)
		if !bl { //1001为文件不存在;1002为读取失败
			Log.Error("读取缓存文件失败")
			go iu.createCacheFile() //重建缓存文件
			return
		}

		jsonStr := da.(string)
		data := map[string]map[string]string{}
		err := json.Unmarshal([]byte(jsonStr), &data)
		if err != nil {
			Log.Error("读取缓存内容异常:", err)
			return
		}

		if len(data) > 3 {
			cacheSetNotLogin = data["cacheSetNotLogin"]
			cacheSubSetNotLogin = data["cacheSubSetNotLogin"]
			cacheSetLogin = data["cacheSetLogin"]
			cacheSubSetLogin = data["cacheSubSetLogin"]
			return
		}
	}

	iu.createCacheFile() //创建缓存文件
}

/**
 * 创建缓存文件
 * @return
 */
func (iu IgnoreUrlService) createCacheFile() {
	me := iu.allIgnoreUrlList()
	if !me.Success {
		return
	}

	cacheSetNotLogin = map[string]string{}    //路径缓存(全部)免登录
	cacheSubSetNotLogin = map[string]string{} //路径缓存(仅包含/*的路径)免登录
	cacheSetLogin = map[string]string{}       //路径缓存(全部)必须登录
	cacheSubSetLogin = map[string]string{}    //路径缓存(仅包含/*的路径)必须登录

	list := me.Data.([]SimpleIgonoreURL)
	for _, simpleIgonoreURL := range list {
		IgnoreUrl := simpleIgonoreURL.IgnoreUrl
		if strings.TrimSpace(IgnoreUrl) == "" {
			continue
		}

		array := strings.Split(IgnoreUrl, ";")
		for _, val := range array {
			val = strings.TrimSpace(val)
			if val == "" {
				continue
			}

			if simpleIgonoreURL.MustLogin != 1 {
				userType := ""
				if simpleIgonoreURL.OnlyUserType != "" {
					userType = ";" + simpleIgonoreURL.OnlyUserType + ";"
				}

				cacheSetNotLogin[val] = userType
			} else {
				userType := ""
				if simpleIgonoreURL.OnlyUserType != "" {
					userType = ";" + simpleIgonoreURL.OnlyUserType + ";"
				}

				cacheSetLogin[val] = userType
			}

			if !strings.Contains(val, "/*") { //如果不包含*,说明没有下级
				continue
			}

			val = strings.Replace(val, "/*", "", -1)
			if simpleIgonoreURL.MustLogin != 1 { //如果包含*,说明要忽略其下级
				userType := ""
				if simpleIgonoreURL.OnlyUserType != "" {
					userType = ";" + simpleIgonoreURL.OnlyUserType + ";"
				}

				cacheSubSetNotLogin[val] = userType
			} else {
				userType := ""
				if simpleIgonoreURL.OnlyUserType != "" {
					userType = ";" + simpleIgonoreURL.OnlyUserType + ";"
				}

				cacheSubSetLogin[val] = userType
			}
		}
	}

	data := map[string]map[string]string{
		"cacheSetNotLogin":    cacheSetNotLogin,
		"cacheSubSetNotLogin": cacheSubSetNotLogin,
		"cacheSetLogin":       cacheSetLogin,
		"cacheSubSetLogin":    cacheSubSetLogin,
	}

	ret_json, _ := json.Marshal(data)
	fileutil.Save(string(ret_json), "./temp/cache/IgnoreUrl/IgnoreUrl.json") //写入文件，以便用户权限验证模块使用
}

/**
 * 清理缓存
 * @param ctx
 * @param cacheName
 * @param user
 * @return
 */
func (iu IgnoreUrlService) ClearCache(ctx ginutil.Context, cacheName, user string) *msgentity.MsgEntity {
	cacheSetNotLogin = map[string]string{}
	cacheSubSetNotLogin = map[string]string{}
	cacheSetLogin = map[string]string{}
	cacheSubSetLogin = map[string]string{}

	Path := "./temp/cache/IgnoreUrl/IgnoreUrl.json"
	fileutil.Del(Path)

	iu.initCacheMap() //初始化缓存,有必要进行,否则会导致拦截器进入访问时第一个会慢

	return msgentity.Success(8999, "请求清理缓存成功！")
}

// 所有不拦截路径
func (iu IgnoreUrlService) allIgnoreUrlList() *msgentity.MsgEntity {
	me := iu.allIgnoreUrlListByFile()
	if me.Success {
		return msgentity.Success(me.Data, "读取配置数据成功")
	}

	return iu.allIgnoreUrlListByDb()
}

// 所有不拦截路径(从数据库获取)
func (iu IgnoreUrlService) allIgnoreUrlListByDb() *msgentity.MsgEntity {
	text := gorm.ReplaceVariable(`SELECT ignore_url, must_login, only_user_type FROM ${BaseSystem}ignore_url WHERE state > 0 ORDER BY must_login`)

	rows, err := gorm.Raw(text).Rows()
	if err != nil {
		Log.Error("查询发生异常:", err)
		return msgentity.Err(7002, "查询发生异常:", err)
	}

	defer rows.Close()

	res := gorm.ScanRows2mapI(rows)
	if res == nil {
		Log.Error("查询后转换数据发生异常")
		return msgentity.Err(7003, "查询后转换数据发生异常")
	}

	if len(res) < 1 {
		return msgentity.Err(7004, "数据不存在！")
	}

	result := []SimpleIgonoreURL{}

	for _, obj := range res {
		result = append(result, SimpleIgonoreURL{
			IgnoreUrl:    toStr(obj["ignore_url"]),
			MustLogin:    obj["must_login"].(int64),
			OnlyUserType: toStr(obj["only_user_type"]),
		})
	}

	return msgentity.Success(result, "查询成功")
}

// 转换字符串
func toStr(data interface{}) string {
	if data == nil {
		return ""
	}

	switch obj := data.(type) {
	case []uint8:
		return byte2Str(obj)
	default:
		return fmt.Sprintf("%v", obj)
	}
}

// Byte转Str
func byte2Str(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

// 所有不拦截路径(从文件获取)
func (iu IgnoreUrlService) allIgnoreUrlListByFile() *msgentity.MsgEntity {
	result := []SimpleIgonoreURL{}

	bl, _, data := fileutil.ReadFromFile("./config/InterceptorIgnore.json")
	if bl {
		var list []SimpleIgonoreURL
		err := json.Unmarshal([]byte(data.(string)), &list)
		if err != nil {
			Log.Error("InterceptorIgnore.json文件转换出错：", err)
		} else {
			result = append(result, list...)
		}
	}

	bl, _, data = fileutil.ReadFromFile("./config/InterceptorIgnoreLogined.json")
	if bl {
		var list []SimpleIgonoreURL
		err := json.Unmarshal([]byte(data.(string)), &list)
		if err != nil {
			Log.Error("InterceptorIgnoreLogined.json文件转换出错：", err)
		} else {
			result = append(result, list...)
		}
	}

	return msgentity.Success(result, "读取成功")
}
